from myeth_env import w3
import web3
from eth_account import Account
from eth_account.local import LocalAccount
from eth_keys import keys
from hexbytes import HexBytes
import json
import common 
import base64
import solc
from binascii import b2a_hex, a2b_hex,b2a_base64,a2b_base64
from db import DBHelper
from common.strtools import hex_add_0x,hex_remove_0x

# print(len(list(w3.eth.accounts)))
# print(list(w3.eth.accounts))

class MyTransaction:
    def __init__(self,txn_hash:HexBytes):
        self.txn_hash = txn_hash
        # txn_receipt = w3.eth.getTransactionReceipt(txn_hash)
        # self.txn_receipt_dct = dict(txn_receipt)
        self.txn = w3.eth.getTransaction(txn_hash)

    # def get_txn_dict(self):
    #     return w3.eth.getTransactionReceipt(self.txn_hash)

    def get_data(self):
        data = self.txn.input
        data = a2b_hex(hex_remove_0x(data))
        return data

    @classmethod
    def send_txn(cls,account:LocalAccount,data:bytes,ret_str_format:bool=False):
        """
        ret_str_format 为 True 表示将结果的 HexBytes 转为十六进制字符串（开头有'0x'）
        """
        acct = account
        nonce = w3.eth.getTransactionCount(acct.address)
        raw_txn = {
            "from":acct.address,
            "to":acct.address, # 自己发自己；如果没有，则表示创建账户
            "nonce": nonce,
            "gas": 1728712,
            "gasPrice": w3.toWei('21', 'gwei'),
            # "data": data.encode("utf-8")
            "data":data
        }
        signed = acct.signTransaction(raw_txn)
        txn_hash_HexBytes:HexBytes = w3.eth.sendRawTransaction(signed.rawTransaction)
        if ret_str_format:
            return txn_hash_HexBytes.hex()
        else:
            return txn_hash_HexBytes

class MyContract:
    def __init__(self,src:str,is_DO_contract:bool):
        self.is_DO_contract = is_DO_contract
        self.src = src
        self.compiled_sol:dict = solc.compile_source(self.src)
        """
            contract_interface 是一个 dict，有以下 key
            ['abi', 'asm', 'bin', 'bin-runtime', 'clone-bin', 'devdoc', 'opcodes', 'userdoc', 'ast']
        """
        self.contract_interface = self._get_contract_interface()
        self.contract_abi = self.contract_interface['abi']
        # self.deploy_txn_dct:dict = None
        self.contract_address = None
        # self.txn_hash:HexBytes = None # 获取16进制串要 self.txn_hash.hex()
        self.contract = None 
        self.__Deploaded = False

    @classmethod
    def construct_MyContract_from_DB(cls,account_address,is_DO_contract:bool):
        try:
            contract_record = DBHelper.get_contract_with_acct_addr(
                account_address,is_DO_contract)
            _mycon = cls(contract_record.contract_src,is_DO_contract)
            _mycon.contract_address = contract_record.contract_address
            _mycon.contract = w3.eth.contract(
                address=contract_record.contract_address,
                abi=json.loads(contract_record.contract_abi)
            )
            # 如果在数据库中，就认为已经部署过了
            _mycon.__Deploaded = True
            return _mycon
        except AssertionError:
            return None

    @classmethod
    def from_src_file(cls,src_path:str,is_DO_contract:bool):
        with open(src_path,"r") as fr:
            src = fr.read() 
        return cls(src,is_DO_contract)

    @classmethod
    def get_deployed_contract(cls,contract_address:str,abi):
        if isinstance(abi,str):
            abi = json.loads(abi)
        _con = w3.eth.contract(
            address=contract_address,
            abi=abi)
        return _con

    def deploy_contract(self,account:LocalAccount,redeploy:bool = False):
        # 如果不要求重新部署
        # 检查是否已经部署(已部署，则直接返回构造好的 contract)
        if not redeploy and self.__Deploaded:
            assert self.contract
            return self.contract
        contract_interface = self.contract_interface
        acct = account # 别名
        _con = w3.eth.contract(
            abi=contract_interface['abi'],
            bytecode=contract_interface['bin'])
        nonce = w3.eth.getTransactionCount(acct.address)
        # print(base64.b64encode(nonce))
        # constructor(acct.address)
        construct_txn = _con.constructor().buildTransaction({ 
            'from': acct.address,
            'nonce': nonce,
            'gas': 1728712,
            'gasPrice': w3.toWei('21', 'gwei')})
        signed = acct.signTransaction(construct_txn)
        txn_hash = w3.eth.sendRawTransaction(signed.rawTransaction)
        # return txn_hash
        txn_receipt = w3.eth.getTransactionReceipt(txn_hash)
        # self.deploy_txn_dct = dict(txn_receipt)
        deploy_txn_dct = dict(txn_receipt)
        # txn_receipt["contractAddress"]是合约address
        self.contract_address = deploy_txn_dct["contractAddress"] 
        self.txn_hash = deploy_txn_dct["transactionHash"]
        self.contract = self._get_deployed_contract()
        # ==================
        DBHelper.save_contract(
            owner_acct_address = acct.address,
            contract_address = self.contract_address,
            contract_abi = json.dumps(self.contract_abi),
            contract_src = self.src,
            is_DO_contract = self.is_DO_contract
        )
        # ==================
        self.__Deploaded = True
        return self.contract

    def _get_deployed_contract(self):
        # contract_interface = self.contract_interface
        _con = w3.eth.contract(
            address=self.contract_address,
            abi=self.contract_abi)
        return _con
        
    def _get_contract_interface(self):
        # contract_id, contract_interface = self.compiled_sol.popitem()
        if self.is_DO_contract:
            contract_name = common.args.DOContract
        else:
            contract_name = common.args.DUContract
        _k = None
        for k in self.compiled_sol.keys():
            if k.endswith(contract_name):
                _k = k
        contract_interface = self.compiled_sol[_k]
        return contract_interface


    
class MyAccountHelper:
    @classmethod
    def new_account(cls,keyPassword:str = ""):
        """
            输入密码 keyPassword，生成以太坊账号
            账号密钥将以 keyPassword 为口令加密
        """
        account = w3.eth.account.create(keyPassword)
        # print(account)
        # account = Account().create()
        privateKey = account._key_obj
        publicKey = privateKey.public_key
        address = publicKey.to_checksum_address()
        common.print_msg("\n\t生成账号：{}".format(address))
        # 存入"钱包"
        wallet = Account.encrypt(account.privateKey, keyPassword)
        accountFilePath = common.path.getAccountKeyPath("{}.json".format(
            hex_add_0x(address).lower()
        ))
        with open(accountFilePath, "w") as fw:
            json.dump(wallet, fw)
        common.print_msg("\n\t账号密钥文件：{}\n\t请记住密码".format(accountFilePath))
        return address

    @classmethod
    def get_account_from_private_key(cls,private_key):
        """
            private_key 如果是 keys.PrivateKey 也行
            因为会被 str 转换为字符串
        """
        return Account().privateKeyToAccount(str(private_key))
        
    @classmethod
    def get_private_key(cls,accountAddress: str, keyPassword: str = ""):
        """
            输入 accountAddress
            获取对应文件中的账号密钥
        """
        address = accountAddress
        accountFilePath = common.path.getAccountKeyPath("{}.json".format(
            hex_add_0x(address).lower()
        ))
        with open(accountFilePath, "r") as fr:
            wallet = json.load(fr)
        common.print_msg("\n\t账号密钥文件：{}".format(accountFilePath))
        privateKey = Account.decrypt(wallet,keyPassword)
        return keys.PrivateKey(privateKey) # 这个传入的是字节
        # return cls.get_private_key_from_str(privateKey)

    @classmethod
    def get_private_key_from_str(cls,private_key_str:str):
        private_key_bytes = a2b_hex(private_key_str)
        return keys.PrivateKey(private_key_bytes)


if __name__=="__main__":
    from pprint import pprint 

    # c = solc.compile_files(["solidity/helloworld.sol"])
    # # pprint(c)
    # # contract_id, contract_interface = c.popitem()
    # # print(contract_id)
    # # print(type(c))
    # contract_id, contract_interface = c.popitem()
    # a = list()
    # for k in contract_interface:
    #     a.append(k)
    # print(a)
    # """
    #     ['abi', 'asm', 'bin', 'bin-runtime', 'clone-bin', 'devdoc', 'opcodes', 'userdoc', 'ast']
    # """


    # register("123")
    # pk = get_private_key("0xCC846Dd233802Fdfe7f78AfB45eB5baA87FCB3E7","123")
    # print(str(pk))
    # print(type(pk))

    # c = compile_sol_src_file(common.args.DOContractFile)
    # # print(json.dumps(c,indent=1))
    # cc = deploy_contract(w3,c[1])
    # print(cc.functions.greet().call())

    # myContract = MyContract.from_src_file(common.args.DOContractFile,True)
    # pk = "1eb8e2955a7c928c1d5b4568c0ea42a3d3f31968f49efc40a1351e3b255f00fe"
    # acct = MyAccountHelper.get_account_from_private_key(pk)
    # cc = myContract.deploy_contract(acct)
    # from pprint import pprint
    # # pprint(dict(cc))
    # pprint(myContract.contract_interface["abi"])
    # pprint(type(myContract.contract_interface["abi"]))
    # print("hello")

    # print(MyAccountHelper.new_account("123"))

    # pk = "1eb8e2955a7c928c1d5b4568c0ea42a3d3f31968f49efc40a1351e3b255f00fe"
    # acct = MyAccountHelper.get_account_from_private_key(pk)
    # MyTransaction.send_txn(acct,b"hahaha")

    data = MyTransaction(HexBytes("0xf3cb4e536dd711699aebb1b9d20d87b0fe5e86448e911a16d83008bc7905f780")).get_data()
    print(data)


